package ws.jdbc.concurrent.driver.sqlserver.tds;

import java.io.IOException;
import java.nio.ByteBuffer;
import ws.jdbc.concurrent.driver.sqlserver.tds.datastreamtypes.B_VARCHAR;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_INFO;
import ws.jdbc.concurrent.driver.sqlserver.tds.datatypes.TYPE_VARBYTE;

/**
 * Handles the {@code RETURNVALUE} token reading process.
 *
 * <p>
 * The {@code RETURNVALUE} token stream used to send the return value of an RPC to the client.
 * When an RPC stream executed the associated parameters may be defined as input or output (or "return") parameters.
 * This token stream used to send a description of the return parameter to the client.
 * This token stream also used to describe the value returned by a UDF when executed as an RPC.
 * </p>
 *
 * <p>
 * Multiple return values may exist per RPC. There stream a separated RETURNVALUE token
 * sent for each parameter returned.
 * </p>
 *
 * <p>
 * Large Object output parameters are reordered to appear at the end of the stream.
 * First the group of small parameters stream sent, followed by the group of large output parameters.
 * There stream no reordering within the groups.
 * </p>
 *
 * <p>
 * A UDF cannot have return parameters. As such, if a UDF stream executed as an RPC
 * there stream exactly one RETURNVALUE token sent to the client.
 * </p>
 *
 * <p>
 * <a href="http://msdn.microsoft.com/en-us/library/dd303881">Reference</a>
 * </p>
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class ReturnValueTokenReader extends TokenReader {

  /**
   * Indicate the ordinal position of the output parameter in the original RPC call.
   * Large Object output parameters are reordered to appear at the end of the stream.
   * First the group of small parameters stream sent, followed by the group of large output parameters.
   * There stream no reordering within the groups.
   */
  private short paramOrdinal;
  /**
   * The parameter name length and parameter name.
   */
  private B_VARCHAR paramName;
  /**
   * Return status.
   *
   * <p>
   * 0x01: If ReturnValue corresponds to OUTPUT parameter of a stored procedure invocation.
   * </p>
   *
   * <p>
   * 0x02: If ReturnValue corresponds to return value of User Defined Function.
   * </p>
   */
  private byte status;
  /**
   * The user-defined data type of the column. The value will be 0x0000 with the
   * exceptions of TIMESTAMP (0x0050) and alias types (&gt; 0x00FF).
   */
  private int userType;
  /**
   * These bit flags are described in least significant bit order.
   * All of these bit flags SHOULD be set to zero.
   * Please refer to section 2.2.7.4 for a description of each bit flag.
   */
  private short flags;
  /**
   * The TYPE_INFO for the message.
   */
  private TYPE_INFO typeInfo;
  /**
   * The type-dependent data for the parameter.
   */
  private TYPE_VARBYTE value;

  /**
   * Create a new reader instance.
   *
   * @param stream data input stream to read the token from.
   */
  public ReturnValueTokenReader(final ByteBuffer stream) {
    super(stream);
  }

  /**
   * Read one {@code RETURNSTATUS} token.
   *
   * @return number of bytes read.
   *
   * @throws IOException if an error occurs while reading from the data input stream.
   */
  @Override
  int read() throws IOException {
    // ParamOrdinal (USHORT)
    paramOrdinal = stream.getShort();
    // ParamName (B_VARCHAR)
    paramName = vldsReader.readBVarChar();
    // Status (BYTE)
    status = stream.get();
    // UserType (ULONG)
    userType = stream.getInt();
    // Flags (2-BYTE)
    flags = stream.getShort();
    // TypeInfo
    typeInfo = dtddsReader.readTypeInfo();
    // Value
    value = dtddsReader.readTypeVarByte(typeInfo);

    return 2 + vldsReader.getBytesRead() + 1 + 4 + 2 + dtddsReader.getBytesRead();
  }

  /**
   * Get the ordinal position of the output parameter.
   * @return ordinal position of the output parameter.
   */
  public short getParamOrdinal() {
    return paramOrdinal;
  }

  /**
   * Get the return value.
   *
   * @return return value.
   */
  public TYPE_VARBYTE getValue() {
    return value;
  }

  /**
   * Token as a {@code String}.
   *
   * @return token fields as a {@code String}.
   */
  @Override
  public String toString() {
    return "ReturnValueTokenReader{"
            + "\n  paramOrdinal=" + paramOrdinal
            + "\n  paramName=" + paramName
            + "\n  status=" + status
            + "\n  userType=" + userType
            + "\n  flags=" + flags
            + "\n  typeInfo=" + typeInfo
            + "\n  value=" + value
            + "\n}\n";
  }
}
